import copy

from command_parser.command_generate import GenCommand
from command_parser.gcc_parse.gcc_option_filter import GCCFilter
from utils.common_utils import *

class GCCGenCommand(GenCommand):
    def __init__(self, context=None, cwd=""):
        super().__init__(context=context, cwd=cwd)
        self.command_filter = GCCFilter(context=self.context)
    
        
    def generate_divided_compile_command(self, compiler="", options=[]):
        
        if not compiler or not options:
            raise Exception("It occurs fails when generate_divided_compile_command")
        
        command_line = [compiler]
        local_options = copy.deepcopy(options)
        
        self.command_filter.filter_link_options(options=local_options)
        self.command_filter.append_option_compile(options=local_options)
        # We need just filter link related options
        # self.command_filter.filter_command(options=options)
        
        for option in local_options:
            command_line.extend(option.get_option())
        
        return command_line
    
    def generate_divided_link_command(self, compiler="", options=[]):
        if not compiler or not options:
            raise Exception("It occurs fails when generate_divided_compile_command")
        
        command_line = [compiler]
        
        local_options = copy.deepcopy(options)
        
        self.command_filter.filter_compile_options(options=local_options)
        
        for option in local_options:
            command_line.extend(option.get_option())
        
        return command_line
    
    def generate_clang_ast_command(self, parser=None, compiler="", options=[]):
        '''
            when call this function, we should already replaced the output option
            This command use the custom gcc header search path
        '''
        if not compiler or not options or not parser:
            raise Exception("It occurs fails when generate_clang_ast_command")
        
        maple_compiler = self.context.MAPLE_CLANG
        if is_cpp_compiler(compiler=compiler):
            maple_compiler = self.context.MAPLE_CLANGCPP
        
        command_line = [maple_compiler]
        
        local_options = copy.deepcopy(options)
        # self.command_filter.filter_original_options(cwd=self.cwd, options=local_options, compiler=compiler)
        self.command_filter.filter_link_options(options=local_options)
        self.command_filter.filter_compile_options(options=local_options)
        self.command_filter.append_option_U(options=options)
        # first, we use the Maple gcc version headers to generate clang ast
        self.command_filter.append_option_include_custom_path(options=local_options)
        self.command_filter.append_option_include_path(compiler=self.context.MAPLE_GCC, options=local_options)
        # self.command_filter.append_option_target(options=local_options)
        self.command_filter.append_option_debug(options=local_options)
        self.command_filter.append_option_fpic(options=local_options)
        self.command_filter.append_option_flto(options=local_options)
        self.command_filter.append_option_wno_error(options=local_options)
        self.command_filter.append_clang_spec_options(options=local_options)
        self.command_filter.append_option_clang_ast(options=local_options)
        
        self.command_filter.filter_transform_option(options=local_options)
        self.command_filter.filter_command(local_options)
        
        for opt in local_options:
            command_line.extend(opt.get_option())
        
        return command_line
    
    def generate_clang_ast_command_with_sys_headers(self, parser=None, compiler="", options=[]):
        if not compiler or not options or not parser:
            raise Exception("It occurs fails when generate_clang_ast_command")
        
        maple_compiler = self.context.MAPLE_CLANG
        if is_cpp_compiler(compiler=compiler):
            maple_compiler = self.context.MAPLE_CLANGCPP
        
        command_line = [maple_compiler]
        
        local_options = copy.deepcopy(options)
        # self.command_filter.filter_original_options(cwd=self.cwd, options=local_options, compiler=compiler)
        self.command_filter.filter_link_options(options=local_options)
        self.command_filter.filter_compile_options(options=local_options)
        self.command_filter.append_option_U(options=options)
        # second, we use the system gcc headers to generate clang ast
        self.command_filter.append_option_include_path(compiler=compiler, options=local_options)
        # self.command_filter.append_option_target(options=local_options)
        self.command_filter.append_option_debug(options=local_options)
        self.command_filter.append_option_fpic(options=local_options)
        self.command_filter.append_option_flto(option=local_options)
        self.command_filter.append_option_wno_error(options=local_options)
        self.command_filter.append_clang_spec_options(options=local_options)
        self.command_filter.append_option_clang_ast(options=local_options)
        
        self.command_filter.filter_transform_option(options=local_options)
        self.command_filter.filter_command(local_options)
        
        for opt in local_options:
            command_line.extend(opt.get_option())
        
        return command_line
    
    def generate_retry_clang_ast_command(self, parser=None, compiler="", options=[]):
        '''
            when call this function, we should already replaced the output option
        '''
        if not compiler or not options or not parser:
            raise Exception("It occurs fails when generate_clang_ast_command")
        
        maple_compiler = self.context.MAPLE_CLANG
        if is_cpp_compiler(compiler=compiler):
            maple_compiler = self.context.MAPLE_CLANGCPP
        
        command_line = [maple_compiler]
        
        local_options = copy.deepcopy(options)
        # self.command_filter.filter_original_options(cwd=self.cwd, options=local_options, compiler=compiler)
        self.command_filter.filter_link_options(options=local_options)
        self.command_filter.filter_compile_options(options=local_options)
        self.command_filter.append_option_U(options=options)
        # self.command_filter.append_option_include_path(compiler=compiler, options=local_options)
        # last, we try to use clang ast to generate ast
        # self.command_filter.append_option_target(options=local_options)
        self.command_filter.append_option_debug(options=local_options)
        self.command_filter.append_option_fpic(options=local_options)
        self.command_filter.append_option_flto(option=local_options)
        self.command_filter.append_option_wno_error(options=local_options)
        self.command_filter.append_clang_spec_options(options=local_options)
        self.command_filter.append_option_clang_ast(options=local_options)
        
        self.command_filter.filter_transform_option(options=local_options)
        self.command_filter.filter_command(local_options)
        
        for opt in local_options:
            command_line.extend(opt.get_option())
        
        return command_line
        
        
    def generate_maple_ir_command(self, parser=None, compiler="", input_file=""):
        '''
            This function should be used after generate_clang_ast_command, and use the output of
            ast command as this generating command input.
        '''
        
        if not parser or not compiler or not input_file:
            raise Exception("It occurs fails when generate_maple_ir_command")
        
        compiler = self.context.MAPLE_BIN
        command_line = [compiler]
        command_line.append(input_file)
        
        output_file = os.path.join(self.context.MAPLE_DIR, os.path.splitext(os.path.basename(input_file))[0] + self.context.MAPLE_EXT)
        command_line.extend(["-o", output_file, "--enable-variable-array"])
        
        return command_line
    
    def generate_assemble_maple_ir_command(self, parser=None, compiler="", options=[]):
        
        
        ast_command = self.generate_clang_ast_command(parser=parser, compiler=compiler, options=options)
        
        output_opt = parser.get_output_options()
        output_file = output_opt[0].parameter[0]
        dst_md5 = os.path.splitext(os.path.basename(output_file))[0]
        ast_path = os.path.join(self.context.AST_DIR, dst_md5 + self.context.AST_EXT)
        
        ir_command = self.generate_maple_ir_command(parser=parser, compiler=self.context.MAPLE_BIN, input_file=ast_path)
        
        return [ast_command, ir_command]
    
    
    def generate_assemble_maple_ir_command_with_sys_headers(self, parser=None, compiler="", options=[]):
        ast_command = self.generate_clang_ast_command_with_sys_headers(parser=parser, compiler=compiler, options=options)
        
        output_opt = parser.get_output_options()
        output_file = output_opt[0].parameter[0]
        dst_md5 = os.path.splitext(os.path.basename(output_file))[0]
        ast_path = os.path.join(self.context.AST_DIR, dst_md5 + self.context.AST_EXT)
        
        ir_command = self.generate_maple_ir_command(parser=parser, compiler=self.context.MAPLE_BIN, input_file=ast_path)
        
        return [ast_command, ir_command]
    
    def generate_assemble_retry_maple_ir_command(self, parser=None, compiler="", options=[]):
        
        
        ast_command = self.generate_retry_clang_ast_command(parser=parser, compiler=compiler, options=options)
        
        output_opt = parser.get_output_options()
        output_file = output_opt[0].parameter[0]
        dst_md5 = os.path.splitext(os.path.basename(output_file))[0]
        ast_path = os.path.join(self.context.AST_DIR, dst_md5 + self.context.AST_EXT)
        
        ir_command = self.generate_maple_ir_command(parser=parser, compiler=self.context.MAPLE_BIN, input_file=ast_path)
        
        return [ast_command, ir_command]
    
    
    # the functions belowed is for test maple ir
    def generate_compile_command(self, parser=None, compiler="", options=[]):
        output_opt = parser.get_input_options()[0]
        output_file = output_opt.parameter[0]
        md5_code = os.path.splitext(os.path.basename(output_file))[0]
        input_file = os.path.join(self.context.MAPLE_DIR, md5_code + self.context.MAPLE_EXT)
        output_file = os.path.join(self.context.ASSEMBLE_DIR, md5_code + self.context.ASSEMBLE_EXT)
        
        command = [compiler, '-O0','--target=aarch64-gnu', '-c', input_file, '-o', output_file]
        return command    
    
    def generate_assembly_command(self, parser=None, compiler="", options=[]):
        
        
        output_opt = parser.get_output_options()[0]
        output_file = output_opt.parameter[0]
        md5_code = os.path.splitext(os.path.basename(output_file))[0]
        src_file = os.path.join(self.context.ASSEMBLE_DIR, md5_code + self.context.ASSEMBLE_EXT)
        
        input_opt = parser.get_input_options()[0]
        input_opt.parameter = [src_file]
        
        command = [compiler]
        for option in options:
            if option.option == '-x':
                continue
            command.extend(option.get_option())
        
        return command